package reference

import (
	"encoding/json"
	"fmt"
	"github.com/hpcloud/tail"
	"github.com/leonscriptcc/bondman/configs"
	"github.com/leonscriptcc/bondman/tools"
	"github.com/tidwall/gjson"
	"io"
	"log"
	"strconv"
)

// TailTool 日志文件追踪工具
type TailTool struct {
	filePath string
	config   tail.Config
	msgs     chan string
}

func NewTailTool() *TailTool {
	return &TailTool{
		filePath: configs.Parameters.LogPath,
		config: tail.Config{
			ReOpen:    true,                                          // 重新打开
			Follow:    true,                                          // 是否跟随
			Location:  &tail.SeekInfo{Offset: 0, Whence: io.SeekEnd}, // 从文件的哪个地方开始读
			MustExist: false,                                         // 文件不存在不报错
			Poll:      true,
		},
		msgs: make(chan string, 1),
	}
}

// GetMsgs 获取消息通道
func (t *TailTool) GetMsgs() chan string {
	return t.msgs
}

// Start 启动tail
func (t *TailTool) Start() {
	// 执行tail命令
	Tails, err := tail.TailFile(t.filePath, t.config)
	if err != nil {
		fmt.Println("tail.TailFile err:", err)
		return
	}
	for {
		// 获取到文件中新增的行
		line, ok := <-Tails.Lines
		if !ok {
			panic("tails chan is closed err:%v")
		}

		// 如果格式不合规，直接结束处理
		var msg LogMsg
		if err = json.Unmarshal([]byte(line.Text), &msg); err != nil {
			log.Println("json.Unmarshal err:", err, ",msg:", line.Text)
			continue
		}

		// 处理日志内容别名
		if configs.Parameters.LogFiledAlias.MsgAlias != "" {
			msg.Msg = gjson.Get(line.Text, configs.Parameters.LogFiledAlias.MsgAlias).String()
		}

		// 只输出error和fatal级别的日志
		if !msg.isAlarmLevel() {
			continue
		}

		// 校验缓存是否已经存在
		// 避免重复发送
		if !tools.CacheMan.GSet(msg.GetPendingMsg()) {
			continue
		}

		// 推送到消息通道，等待消息发送
		t.msgs <- msg.GetPendingMsg()
	}
}

// LogMsg 日志格式
type LogMsg struct {
	Level   string `json:"level"`
	Ts      string `json:"ts"`
	Caller  string `json:"caller"`
	Msg     string `json:"msg"`
	TraceID int64  `json:"traceID"`
}

// GetPendingMsg 获取待发送消息
func (l *LogMsg) GetPendingMsg() string {
	// 优先使用traceID
	if l.TraceID > 0 {
		return strconv.FormatInt(l.TraceID, 10)
	}

	// 没有traceID的情况下处理msg长度
	if len(l.Msg) > 20 {
		return string([]rune(l.Msg)[:20]) + "..."
	}

	return l.Msg
}

// isAlarmLevel 校验是否达到了报警等级
func (l *LogMsg) isAlarmLevel() bool {
	if l.Level != "error" && l.Level != "fatal" {
		return false
	}
	return true
}
